package org.example.lotterysystem.service.impl;
import org.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import org.example.lotterysystem.common.exception.ServiceException;
import org.example.lotterysystem.common.utils.JacksonUtil;
import org.example.lotterysystem.common.utils.RedisUtil;
import org.example.lotterysystem.controller.param.CreateActivityParam;
import org.example.lotterysystem.controller.param.CreatePrizeByActivityParam;
import org.example.lotterysystem.controller.param.CreateUserByActivityParam;
import org.example.lotterysystem.controller.param.PageParam;
import org.example.lotterysystem.dao.dataobject.ActivityDO;
import org.example.lotterysystem.dao.dataobject.ActivityPrizeDO;
import org.example.lotterysystem.dao.dataobject.ActivityUserDO;
import org.example.lotterysystem.dao.dataobject.PrizeDO;
import org.example.lotterysystem.dao.mapper.*;
import org.example.lotterysystem.service.ActivityService;
import org.example.lotterysystem.service.dto.ActivityDTO;
import org.example.lotterysystem.service.dto.ActivityDetailDTO;
import org.example.lotterysystem.service.dto.CreateActivityDTO;
import org.example.lotterysystem.service.dto.PageListDTO;
import org.example.lotterysystem.service.enmus.ActivityPrizeStatusEnum;
import org.example.lotterysystem.service.enmus.ActivityPrizeTiersEnum;
import org.example.lotterysystem.service.enmus.ActivityStatusEnum;
import org.example.lotterysystem.service.enmus.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class ActivityServiceImpl implements ActivityService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    private static final Logger logger= LoggerFactory.getLogger(ActivityServiceImpl.class);
    @Autowired
    private RedisUtil redisUtil;
    /**
     * 活动缓存前置，为了区分业务
     */
    private final String ACTIVITY_PREFIX = "ACTIVITY_";
    /**
     * 活动缓存过期时间
     */
    private final Long ACTIVITY_TIMEOUT = 60 * 60 * 24 * 3L;

    /**
     * 创建活动
     * @param param
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)//涉及多表
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验活动信息是否正确
        checkActivityInfo(param);
        //保存活动信息保存到库中
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);
        //保存活动关联的奖品信息
        List<CreatePrizeByActivityParam> prizeParams = param.getActivityPrizeList();
        List<ActivityPrizeDO> activityPrizeDOList= prizeParams.stream().map(prizeParam->{
            ActivityPrizeDO activityPrizeDO=new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeId(activityPrizeDO.getPrizeId());
            activityPrizeDO.setPrizeAmount(activityPrizeDO.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(activityPrizeDO.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
            return activityPrizeDO;
        }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(activityPrizeDOList);
        //保存活动关联的人员信息
        List<CreateUserByActivityParam> userParams = param.getActivityUserList();
        List<ActivityUserDO> activityUserDOList=userParams.stream().map(userParam->{
            ActivityUserDO activityUserDO=new ActivityUserDO();
            activityUserDO.setActivityId(activityDO.getId());
            activityUserDO.setUserId(userParam.getUserId());
            activityUserDO.setUserName(userParam.getUserName());
            activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
            return activityUserDO;
        }).collect(Collectors.toList());
        //整合完整的活动信息,存放到redis
        //先获取奖品基本属性
        //先获取奖品id
        List<Long> prizeIds=param.getActivityPrizeList().stream().map(CreatePrizeByActivityParam::getPrizeID).distinct().collect(Collectors.toList());
        List<PrizeDO>prizeDOList=prizeMapper.batchSelectByIds(prizeIds);
        //将DO数据转换成DTO类型的数据
        ActivityDetailDTO activityDetailDTO =convertToActivityDetailDTO(activityDO,activityUserDOList,activityPrizeDOList,prizeDOList);
        cacheActivity(activityDetailDTO);
        //构造返回
        CreateActivityDTO createActivityDTO=new CreateActivityDTO();
        createActivityDTO.setActivityId(createActivityDTO.getActivityId());
        return createActivityDTO;

    }

    /**
     * 翻页查询活动列表（基本信息）
     * @return
     */
    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        //获取总量
        int total=activityMapper.count();
        //获取当前页列表
        List<ActivityDO> activityDOList=activityMapper.selectActivityList(param.offset(),param.getPageSize());
        List<ActivityDTO> activityDTOList=activityDOList.stream().map(
                activityDO -> {
                    ActivityDTO activityDTO=new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                }).collect(Collectors.toList());

        return new PageListDTO<>(total,activityDTOList);
    }

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(activityId==null){
            logger.warn("查询活动详情信息失败，activityId为空");
            return null;
        }
        //先向redis中查询数据
        ActivityDetailDTO detailDTO=getActivityFromCache(activityId);
        if(null!=detailDTO){
            logger.info("查询活动详情信息成功，detailDTO={}",JacksonUtil.writeValueAsString(detailDTO));
            return detailDTO;
        }
        //如果数据库查询不到，则分别查询数据，并进行整合
        //查询活动表
        ActivityDO aDO=activityMapper.selectById(activityId);

        //查询活动奖品表
        List<ActivityPrizeDO> activityPrizeDOList=activityPrizeMapper.selectByActivityId(activityId);
        //查询活动人员关联表
        List<ActivityUserDO>activityUserDOList=activityUserMapper.selectByActivityId(activityId);
        //查询奖品表，首先要获取奖品id，奖品id从活动奖品表中查询。
        //获取奖品id
        List<Long> activityIds=activityPrizeDOList.stream().map(ActivityPrizeDO::getActivityId).collect(Collectors.toList());
        List<PrizeDO> prizeDOList=prizeMapper.batchSelectByIds(activityIds);
        //整合信息，将信息存储到redis中
        detailDTO=convertToActivityDetailDTO(aDO,activityUserDOList,activityPrizeDOList,prizeDOList);
        cacheActivity(detailDTO);
        return detailDTO;
    }

    /**
     * 将抽奖活动结果存入缓存
     * @param activityId
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(null==activityId){
            logger.warn("要缓存的活动id为空");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_IS_EMPTY);
        }
        //走到这里就是查询数据库，活动表，奖品表，关联人员，关联奖品表
        //查询活动表
        ActivityDO activityDO = activityMapper.selectById(activityId);
        if(activityDO==null){
            logger.error("要缓存的活动id有误");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITY_ID_ERROR);
        }
        List<ActivityPrizeDO> activityPrizeDOList = activityPrizeMapper.selectByActivityId(activityId);
        List<ActivityUserDO> activityUserDOList = activityUserMapper.selectByActivityId(activityId);
        //查询奖品表，要获取奖品id从activityPrizeDOList获取
        List<Long> prizeIds=activityPrizeDOList.stream().map(ActivityPrizeDO ::getPrizeId).collect(Collectors.toList());
        List<PrizeDO> prizeDOList = prizeMapper.batchSelectByIds(prizeIds);
        //整合数据存入到redis中
        cacheActivity(
                convertToActivityDetailDTO(activityDO,activityUserDOList,activityPrizeDOList,prizeDOList));

    }

    /**
     * 将活动信息存到Redis中
     * @param activityDTO
     */
    private void cacheActivity(ActivityDetailDTO activityDTO) {
        if (null == activityDTO) {
            logger.warn("要缓存的活动信息不存在");
            return;
        }
        try {
            redisUtil.set(ACTIVITY_PREFIX + activityDTO, JacksonUtil.writeValueAsString(activityDTO), activityDTO.getActivityId());
        } catch (Exception e){
            logger.error("缓存活动异常ActivityDetailDTO{}",activityDTO);
        }
    }

    /**
     * 获取redis中的活动信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getActivityFromCache(Long activityId){
        if(activityId==null){
            logger.warn("要获取缓存活动数据activityId为空");
            return null;
        }
        try{
            String s = redisUtil.get(ACTIVITY_PREFIX + activityId);
            if(StringUtils.hasText(s)){
                logger.info("要获取顶顶顶的缓存数据为空!key={}",ACTIVITY_PREFIX+activityId);
            }
            return JacksonUtil.readValue(s,ActivityDetailDTO.class);
        }catch (Exception e){
            logger.error("从缓存中获取信息异常,key={}",ACTIVITY_PREFIX+activityId);
            return null;
        }
    }






    /**
     *
     * @param activityDO   从前端传入的,封装后得到的
     * @param activityUserDOList  这个是关于人员的列表,是从前端传入的,进行的封封装
     * @param activityPrizeDOList   这个是活动关联的奖品列表,也是前端传入的
     * @param prizeDOList 从数据库中查询到的奖品信息的列表
     * @return
     */
    private ActivityDetailDTO convertToActivityDetailDTO(ActivityDO activityDO, List<ActivityUserDO> activityUserDOList, List<ActivityPrizeDO> activityPrizeDOList, List<PrizeDO> prizeDOList) {
        //先将基本的先转换了
        ActivityDetailDTO activityDetailDTO=new ActivityDetailDTO();
        activityDetailDTO.setActivityId(activityDO.getId());
        activityDetailDTO.setActivityName(activityDO.getActivityName());
        activityDetailDTO.setDesc(activityDO.getDescription());
        activityDetailDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
        //下面就是转换列表的东西
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList=activityPrizeDOList.stream().map(
                apDO->{
                    ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(apDO.getPrizeId());
                    //这里从数据库查到的奖品信息是一个列表,所以要获取里面的元素,就需要挨个遍历,同时要判断一下从数据库中查到的数据和前端传入的参数是否一样.
                    //补充的是因为活动奖品关联表里面并没有一些具体的信息,所以要要进行查询
                    Optional<PrizeDO>optionalPrizeDO=prizeDOList.stream().filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId())).findFirst();
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });

                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());
        activityDetailDTO.setPrizeDTOList(prizeDTOList);
        List<ActivityDetailDTO.UserDTO> userDTOList=activityUserDOList.stream().map(auDO->{
            ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
            userDTO.setUserName(auDO.getUserName());
            userDTO.setStatus(ActivityUserStatusEnum.forName(auDO.getStatus()));
            userDTO.setUserId(auDO.getUserId());
            return userDTO;
        }).collect(Collectors.toList());
        activityDetailDTO.setUserDTOList(userDTOList);
        return activityDetailDTO;
    }

    /**
     * 校验活动有效性
     * @param param
     */
    private void checkActivityInfo(CreateActivityParam param) {
        if(param==null){
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }
        //人员id在人员表中
        List<Long> userIds=param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct().collect(Collectors.toList());
        List<Long> existsUserIds = userMapper.selectExistsByIds(userIds);
        userIds.forEach(id->{
            if(existsUserIds.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });
        //奖品id是否存在奖品表中
        List<Long> prizeIds=param.getActivityPrizeList()
                .stream().map(CreatePrizeByActivityParam::getPrizeAmount).distinct().collect(Collectors.toList());
        List<Long> prizeIdList = prizeMapper.selectExistsByIds(prizeIds);
        prizeIds.forEach(id->{
            if(prizeIdList.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });
        //人员数量大于等于奖品数量
        int userAmount=param.getActivityUserList().size();
        long prizeAmount=param.getActivityPrizeList().stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if(userAmount<prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.USER_PRIZE_SET_ERROR);
        }
        //校验活动奖品等级有效性
        param.getActivityPrizeList().forEach(prize->{
            if(null== ActivityPrizeTiersEnum.forName(prize.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_TIERS_ERROR);
            }
        });
    }


}
